package com.moyoutian.leetcode;

/**
 * 1005. K 次取反后最大化的数组和
 * <p>
 * 给你一个整数数组 nums 和一个整数 k ，按以下方法修改该数组：
 * <p>
 * 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
 * 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。
 * <p>
 * 以这种方式修改数组后，返回数组 可能的最大和 。
 * <p>
 * <p>
 * <p>
 * 示例 1：
 * <p>
 * 输入：nums = [4,2,3], k = 1
 * 输出：5
 * 解释：选择下标 1 ，nums 变为 [4,-2,3] 。
 * 示例 2：
 * <p>
 * 输入：nums = [3,-1,0,2], k = 3
 * 输出：6
 * 解释：选择下标 (1, 2, 2) ，nums 变为 [3,1,0,2] 。
 * 示例 3：
 * <p>
 * 输入：nums = [2,-3,-1,5,-4], k = 2
 * 输出：13
 * 解释：选择下标 (1, 4) ，nums 变为 [2,3,-1,5,4] 。
 * <p>
 * <p>
 * 提示：
 * <p>
 * 1 <= nums.length <= 104
 * <p>
 * -100 <= nums[i] <= 100
 * <p>
 * 1 <= k <= 104
 * <p>
 * 通过次数33,142提交次数62,583
 */
public class Demo1005 {
    public static void main(String[] args) {
        int[] nums = {3, -1, 0, 2};
        int k = 3;
        System.out.println(largestSumAfterKNegations(nums, k));
    }

    /**
     * 读题开始时间：9：13
     * <p>
     * 题目分析：
     * 预估时间复杂度，数组和，所无序无规则。最少便利一遍。最好O(n)。
     * k个位置进行翻转，求最大和。
     * 可以暴力循环 求和 取最大。
     * <p>
     * 可以贪心。找到怎样获取最大和的直接方法。
     * 最大和，还需要对原来数字取反。
     * 如果数组中有负数。对负数取反 和肯定增加。
     * 如果没有负数，对字取反，值肯定减少。怎么能减少最少，那么就是对最小的数取反。
     * 综上所述，只需要对最小的k个数取反。但是k可以指向同一个数字，所以呢有负数情况，肯定要指向负数。
     * 无负数情况就是这个最小值取正数，或者反。跟当前要操作取反的次数有关。k-n ，n已经操作过的次数。
     * <p>
     * 所以 需要记录k个最小负数，如果没有负数，就需要记录最小的一个正数。
     *
     * @param nums
     * @param k
     * @return
     */
    public static int largestSumAfterKNegations(int[] nums, int k) {
        //要记录的数组,k个最小负数 -100 <= nums[i] <= 100
        //根据题目可知，数字范围很小，所以直接用这个范围来记录即可
        int[] temp = new int[201];

        int sum = 0;
        //找到绝对值最小的数
        int min = 100;//最小正数初始1
        for (int i = 0; i < nums.length; i++) {
            temp[nums[i] + 100]++;
            sum += nums[i];
            min = Integer.min(min, Math.abs(nums[i]));
        }


        for (int i = 0; i < temp.length; i++) {
            //如果当前数字有次数
            if (temp[i] > 0) {
                //负数
                if (i < 100) {
                    if (temp[i] < k) {
                        //  和-=值*次数*2  ,乘二 是多加的还得在减回来中间是两次
                        sum += (100 - i) * temp[i] * 2;
                    } else {
                        //  和-=值*次数*2  ,乘二 是多加的还得在减回来中间是两次
                        sum += (100 - i) * k * 2;
                        k = 0;
                        break;
                    }
                    //k-=当前次数
                    k -= temp[i];
                } else {
                    break;
                }
            }
        }
        if (k == 0) {
            return sum;
        } else if ((k & 1) == 1) { //奇数
            // sum =sum - 原数 - 原数
            sum -= min * 2;
        }
        return sum;
    }
}
